#ifndef CLUSNET_BASE_FIXEDMEMORYPOOL_H
#define CLUSNET_BASE_FIXEDMEMORYPOOL_H

#include <clusnet/base/Exception.h>
#include <cassert>
#include <cstddef>
#include <cstring>

namespace clusnet
{
namespace base
{

template<size_t SIZE = 1024>
class FixedMemoryPool final
{
    union obj
    {
        union obj* freeListLink_;
        char  buffer_[SIZE];
    };

    static const size_t kDefaultSize = 0x00000080;    // 128
    static const size_t kIncreaseSize = 0x00000400;   // 1024

    public:
        FixedMemoryPool(const FixedMemoryPool<SIZE>&) = delete;
        FixedMemoryPool<SIZE>& operator= (const FixedMemoryPool<SIZE>&) = delete;
        explicit FixedMemoryPool(size_t chunkSize = kDefaultSize, size_t increaseSize = kIncreaseSize)
            : chunkSize_(chunkSize),
              increaseSize_(increaseSize),
              freeObjList_(nullptr)
        {
            assert(chunkSize_ > 0 && increaseSize_ > 0);
            allocate(chunkSize_);
        }

        ~FixedMemoryPool()
        {
            destroy();
        }

    public:
        void* acquire()
        {
            void* result = std::memset(getFree(), 0, sizeof(obj));
            return result;
        }

        void release(void* p)
        {
            deallocate(p);
        }


    private:
        void destroy()
        {
            obj *temp;
            while (freeObjList_ != nullptr)
            {
                temp = freeObjList_;
                freeObjList_ = freeObjList_->freeListLink_;
                free(temp);
            }
            temp = nullptr;
        }

        void deallocate(void* p)
        {
            assert(p != nullptr);
            obj* q = static_cast<obj*>(p);
            q->freeListLink_ = freeObjList_;
            freeObjList_ = q;
        }

        void allocate(size_t size)
        {
            assert(freeObjList_ == nullptr);
            obj *nextObj = nullptr;
            for (size_t i = 0; i < size; ++i)
            {
                obj* currentObj = static_cast<obj *>(::malloc(sizeof(obj)));
                if (currentObj == nullptr)
                {
                    // 释放已分配的内存
                    if (nextObj != nullptr)
                    {
                        nextObj->freeListLink_ = nullptr;
                        destroy();
                    }
                    throw clusnet::base::Exception("malloc failed");
                }

                if (freeObjList_ == nullptr)
                {
                    freeObjList_ = nextObj = currentObj;
                }
                else
                {
                    nextObj->freeListLink_ = currentObj;
                    nextObj = nextObj->freeListLink_;
                }
            }

            nextObj->freeListLink_ = nullptr;
        }

        void* getFree()
        {
            if (freeObjList_ == nullptr)
            {
                allocate(increaseSize_);
            }
            assert(freeObjList_ != nullptr);

            obj* result = freeObjList_;
            freeObjList_ = freeObjList_->freeListLink_;
            return result;
        }

    private:
        size_t  chunkSize_;
        size_t  increaseSize_;
        obj*    freeObjList_;
};
}
}

#endif 
